using System;
using System.Diagnostics;
using Community.CsharpSqlite.Entity;
using unsigned = System.Int32;

namespace Community.CsharpSqlite
{
    using sqlite3_value = Mem;

    public partial class Sqlite3
    {
        /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** Main file for the SQLite library.  The routines in this file
    ** implement the programmer interface to the library.  Routines in
    ** other files are for internal use by SQLite and should not be
    ** accessed by users of the library.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/main_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"
#if  SQLITE_ENABLE_FTS3
//# include "fts3.h"
#endif
#if SQLITE_ENABLE_RTREE
//# include "rtree.h"
#endif
#if SQLITE_ENABLE_ICU
//# include "sqliteicu.h"
#endif

        /*
** The version of the library
*/
#if !SQLITE_AMALGAMATION
        public static string sqlite3_version = SQLITE_VERSION;
#endif

        public static string sqlite3_libversion()
        {
            return sqlite3_version;
        }

        public static string sqlite3_sourceid()
        {
            return SQLITE_SOURCE_ID;
        }

        public static int sqlite3_libversion_number()
        {
            return SQLITE_VERSION_NUMBER;
        }

        public static int sqlite3_threadsafe()
        {
            return Const.SQLITE_THREADSAFE;
        }

#if !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE
/*
** If the following function pointer is not NULL and if
** SQLITE_ENABLE_IOTRACE is enabled, then messages describing
** I/O active are written using this function.  These messages
** are intended for debugging activity only.
*/
//void (*sqlite3IoTrace)(const char*, ...) = 0;
static void sqlite3IoTrace( string X, params object[] ap ) {  }
#endif

        /*
** If the following global variable points to a string which is the
** name of a directory, then that directory will be used to store
** temporary files.
**
** See also the "PRAGMA temp_store_directory" SQL command.
*/
        private static string sqlite3_temp_directory = ""; //char *sqlite3_temp_directory = 0;

        /*
    ** Initialize SQLite.
    **
    ** This routine must be called to initialize the memory allocation,
    ** VFS, and mutex subsystems prior to doing any serious work with
    ** SQLite.  But as long as you do not compile with SQLITE_OMIT_AUTOINIT
    ** this routine will be called automatically by key routines such as
    ** sqlite3_open().
    **
    ** This routine is a no-op except on its very first call for the process,
    ** or for the first call after a call to sqlite3_shutdown.
    **
    ** The first thread to call this routine runs the initialization to
    ** completion.  If subsequent threads call this routine before the first
    ** thread has finished the initialization process, then the subsequent
    ** threads must block until the first thread finishes with the initialization.
    **
    ** The first thread might call this routine recursively.  Recursive
    ** calls to this routine should not block, of course.  Otherwise the
    ** initialization process would never complete.
    **
    ** Let X be the first thread to enter this routine.  Let Y be some other
    ** thread.  Then while the initial invocation of this routine by X is
    ** incomplete, it is required that:
    **
    **    *  Calls to this routine from Y must block until the outer-most
    **       call by X completes.
    **
    **    *  Recursive calls to this routine from thread X return immediately
    **       without blocking.
    */

        public static int sqlite3_initialize()
        {
            //--------------------------------------------------------------------
            // Under C#, Need to initialize some static variables
            //
            if (sqlite3_version == null) sqlite3_version = SQLITE_VERSION;
            if (Global.Sqlite3OpcodeProperty == null) Global.Sqlite3OpcodeProperty = OPFLG_INITIALIZER;
            if (Global.Config == null) Global.Config = Global.Sqlite3Config;
            if (Global.UpperToLower == null) Global.UpperToLower = Global.Sqlite3UpperToLower;
            //--------------------------------------------------------------------


            sqlite3_mutex pMaster; /* The main static mutex */
            int rc; /* Result code */

#if SQLITE_OMIT_WSD
rc = sqlite3_wsd_init(4096, 24);
if( rc!=StatusCode.SQLITE_OK ){
return rc;
}
#endif
            /* If SQLite is already completely initialized, then this call
** to sqlite3_initialize() should be a no-op.  But the initialization
** must be complete.  So isInit must not be set until the very end
** of this routine.
*/
            if (Global.Config.isInit != 0) return StatusCode.SQLITE_OK;

            /* Make sure the mutex subsystem is initialized.  If unable to
      ** initialize the mutex subsystem, return early with the error.
      ** If the system is so sick that we are unable to allocate a mutex,
      ** there is not much SQLite is going to be able to do.
      **
      ** The mutex subsystem must take care of serializing its own
      ** initialization.
      */
            rc = MutexHelper.MutexInit();
            if (rc != 0) return rc;

            /* Initialize the malloc() system and the recursive pInitMutex mutex.
      ** This operation is protected by the STATIC_MASTER mutex.  Note that
      ** MutexAlloc() is called for a static mutex prior to initializing the
      ** malloc subsystem - this implies that the allocation of a static
      ** mutex must not require support from the malloc subsystem.
      */
            pMaster = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_MASTER);
            MutexHelper.MutexEnter(pMaster);
            Global.Config.isMutexInit = 1;
            if (Global.Config.isMallocInit == 0)
            {
                rc = Malloc.MallocInit();
            }
            if (rc == StatusCode.SQLITE_OK)
            {
                Global.Config.isMallocInit = 1;
                if (Global.Config.pInitMutex == null)
                {
                    Global.Config.pInitMutex =
                        MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_RECURSIVE);
                    if (Global.Config.bCoreMutex && Global.Config.pInitMutex == null)
                    {
                        rc = StatusCode.SQLITE_NOMEM;
                    }
                }
            }
            if (rc == StatusCode.SQLITE_OK)
            {
                Global.Config.nRefInitMutex++;
            }
            MutexHelper.MutexLeave(pMaster);
            /* If rc is not StatusCode.SQLITE_OK at this point, then either the malloc
      ** subsystem could not be initialized or the system failed to allocate
      ** the pInitMutex mutex. Return an error in either case.  */
            if (rc != StatusCode.SQLITE_OK)
            {
                return rc;
            }

            /* Do the rest of the initialization under the recursive mutex so
      ** that we will be able to handle recursive calls into
      ** sqlite3_initialize().  The recursive calls normally come through
      ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other
      ** recursive calls might also be possible.
      */
            MutexHelper.MutexEnter(Global.Config.pInitMutex);
            if (Global.Config.isInit == 0 && Global.Config.inProgress == 0)
            {
                Global.Config.inProgress = 1;
#if SQLITE_OMIT_WSD
FuncDefHash *pHash = &GLOBAL(FuncDefHash, Global.Sqlite3GlobalFunctions);
memset( pHash, 0, sizeof( Global.Sqlite3GlobalFunctions ) );
#else
                Global.Sqlite3GlobalFunctions = new FuncDefHash();
                FuncDefHash pHash = Global.Sqlite3GlobalFunctions;
#endif
                sqlite3RegisterGlobalFunctions();
                if (Global.Config.isPCacheInit == 0)
                {
                    rc = sqlite3PcacheInitialize();
                }
                if (rc == StatusCode.SQLITE_OK)
                {
                    Global.Config.isPCacheInit = 1;
                    rc = sqlite3_os_init();
                }
                if (rc == StatusCode.SQLITE_OK)
                {
                    sqlite3PCacheBufferSetup(Global.Config.pPage,
                                             Global.Config.szPage, Global.Config.nPage);
                    Global.Config.isInit = 1;
                }
                Global.Config.inProgress = 0;
            }
            MutexHelper.MutexLeave(Global.Config.pInitMutex);
            /* Go back under the static mutex and clean up the recursive
      ** mutex to prevent a resource leak.
      */
            MutexHelper.MutexEnter(pMaster);
            Global.Config.nRefInitMutex--;
            if (Global.Config.nRefInitMutex <= 0)
            {
                Debug.Assert(Global.Config.nRefInitMutex == 0);
                MutexHelper.MutexFree(ref Global.Config.pInitMutex);
                Global.Config.pInitMutex = null;
            }
            MutexHelper.MutexLeave(pMaster);

            /* The following is just a sanity check to make sure SQLite has
      ** been compiled correctly.  It is important to run this code, but
      ** we don't want to run it too often and soak up CPU cycles for no
      ** reason.  So we run it once during initialization.
      */
#if !NDEBUG
#if !SQLITE_OMIT_FLOATING_POINT
      /* This section of code's only "output" is via Debug.Assert() statements. */
      if ( rc == StatusCode.SQLITE_OK )
      {
        //ulong x = ( ( (ulong)1 ) << 63 ) - 1;
        //double y;
        //Debug.Assert( sizeof( ulong ) == 8 );
        //Debug.Assert( sizeof( ulong ) == sizeof( double ) );
        //memcpy( &y, x, 8 );
        //Debug.Assert( Utility.Sqlite3IsNaN( y ) );
      }
#endif
#endif

            return rc;
        }

        /*
    ** Undo the effects of sqlite3_initialize().  Must not be called while
    ** there are outstanding database connections or memory allocations or
    ** while any part of SQLite is otherwise in use in any thread.  This
    ** routine is not threadsafe.  But it is safe to invoke this routine
    ** on when SQLite is already shut down.  If SQLite is already shut down
    ** when this routine is invoked, then this routine is a harmless no-op.
    */

        public static int sqlite3_shutdown()
        {
            if (Global.Config.isInit != 0)
            {
                sqlite3_os_end();
                sqlite3_reset_auto_extension();
                Global.Config.isInit = 0;
            }
            if (Global.Config.isPCacheInit != 0)
            {
                sqlite3PcacheShutdown();
                Global.Config.isPCacheInit = 0;
            }
            if (Global.Config.isMallocInit != 0)
            {
                Malloc.MallocEnd();
                Global.Config.isMallocInit = 0;
            }
            if (Global.Config.isMutexInit != 0)
            {
                MutexHelper.MutexEnd();
                Global.Config.isMutexInit = 0;
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Set up the lookaside buffers for a database connection.
    ** Return StatusCode.SQLITE_OK on success.
    ** If lookaside is already active, return StatusCode.SQLITE_BUSY.
    **
    ** The sz parameter is the number of bytes in each lookaside slot.
    ** The cnt parameter is the number of slots.  If pStart is NULL the
    ** space for the lookaside memory is obtained from Malloc.sqlite3_malloc().
    ** If pStart is not NULL then it is sz*cnt bytes of memory to use for
    ** the lookaside memory.
    */

        private static int setupLookaside(sqlite3 db, byte[] pBuf, int sz, int cnt)
        {
            //void* pStart;
            //if ( db.lookaside.nOut )
            //{
            //  return StatusCode.SQLITE_BUSY;
            //}
            ///* Free any existing lookaside buffer for this handle before
            //** allocating a new one so we don't have to have space for
            //** both at the same time.
            //*/
            //if ( db.lookaside.bMalloced )
            //{
            //  //Malloc.sqlite3_free( db.lookaside.pStart );
            //}
            ///* The size of a lookaside slot needs to be larger than a pointer
            //** to be useful.
            //*/
            //if ( sz <= (int)sizeof( LookasideSlot* ) ) sz = 0;
            //if ( cnt < 0 ) cnt = 0;
            //if ( sz == 0 || cnt == 0 )
            //{
            //  sz = 0;
            //  pStart = 0;
            //}
            //else if ( pBuf == 0 )
            //{
            //   sz = Utility.ROUND8(sz);
            //  sqlite3BeginBenignMalloc();
            //  pStart = Malloc.sqlite3Malloc( sz * cnt );
            //  sqlite3EndBenignMalloc();
            //}
            //else
            //{
            //  Utility.ROUNDDOWN8(sz);
            //  pStart = pBuf;
            //}
            //db.lookaside.pStart = pStart;
            //db.lookaside.pFree = 0;
            //db.lookaside.sz = (ushort)sz;
            //if ( pStart )
            //{
            //  int i;
            //  LookasideSlot* p;
            //  Debug.Assert( sz > sizeof( LookasideSlot* ) );
            //  p = (LookasideSlot*)pStart;
            //  for ( i = cnt - 1 ; i >= 0 ; i-- )
            //  {
            //    p.pNext = db.lookaside.pFree;
            //    db.lookaside.pFree = p;
            //    p = (LookasideSlot*)&( (byte*)p )[sz];
            //  }
            //  db.lookaside.pEnd = p;
            //  db.lookaside.bEnabled = 1;
            //  db.lookaside.bMalloced = pBuf == 0 ? 1 : 0;
            //}
            //else
            //{
            //  db.lookaside.pEnd = 0;
            //  db.lookaside.bEnabled = 0;
            //  db.lookaside.bMalloced = 0;
            //}
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Return the mutex associated with a database connection.
    */

        private sqlite3_mutex sqlite3_db_mutex(sqlite3 db)
        {
            return db.mutex;
        }

        /*
    ** Configuration settings for an individual database connection
    */

        private static int sqlite3_db_config(sqlite3 db, int op, params object[] ap)
        {
            //va_list ap;
            int rc;
            Custom.VaStart(ap, "");
            switch (op)
            {
                case ConfigOption.SQLITE_DBCONFIG_LOOKASIDE:
                    {
                        var pBuf = (byte[]) Custom.VaArg(ap, "byte[]");
                        var sz = (int) Custom.VaArg(ap, "int");
                        var cnt = (int) Custom.VaArg(ap, "int");
                        rc = setupLookaside(db, pBuf, sz, cnt);
                        break;
                    }
                default:
                    {
                        rc = StatusCode.SQLITE_ERROR;
                        break;
                    }
            }
            Custom.VaEnd(ap);
            return rc;
        }


        /*
    ** Return true if the buffer z[0..n-1] contains all spaces.
    */

        private static bool allSpaces(string z, int iStart, int n)
        {
            while (n > 0 && z[iStart + n - 1] == ' ')
            {
                n--;
            }
            return n == 0;
        }

        /*
    ** This is the default collating function named "BINARY" which is always
    ** available.
    **
    ** If the padFlag argument is not NULL then space padding at the end
    ** of strings is ignored.  This implements the RTRIM collation.
    */

        private static int binCollFunc(
            object padFlag,
            int nKey1, string pKey1,
            int nKey2, string pKey2
            )
        {
            int rc, n;
            n = nKey1 < nKey2 ? nKey1 : nKey2;
            rc = Custom.Memcmp(pKey1, pKey2, n);
            if (rc == 0)
            {
                if ((int) padFlag != 0 && allSpaces(pKey1, n, nKey1 - n) && allSpaces(pKey2, n, nKey2 - n))
                {
                    /* Leave rc unchanged at 0 */
                }
                else
                {
                    rc = nKey1 - nKey2;
                }
            }
            return rc;
        }

        /*
    ** Another built-in collating sequence: NOCASE.
    **
    ** This collating sequence is intended to be used for "case independant
    ** comparison". SQLite's knowledge of upper and lower case equivalents
    ** extends only to the 26 characters used in the English language.
    **
    ** At the moment there is only a UTF-8 implementation.
    */

        private static int nocaseCollatingFunc(
            object NotUsed,
            int nKey1, string pKey1,
            int nKey2, string pKey2
            )
        {
            int n = (nKey1 < nKey2) ? nKey1 : nKey2;
            int r = Utility.Sqlite3StrNICmp(pKey1, pKey2, (nKey1 < nKey2) ? nKey1 : nKey2);
            Helper.UNUSED_PARAMETER(NotUsed);
            if (0 == r)
            {
                r = nKey1 - nKey2;
            }
            return r;
        }

        /*
    ** Return the ROWID of the most recent insert
    */

        public static long sqlite3_last_insert_rowid(sqlite3 db)
        {
            return db.lastRowid;
        }

        /*
    ** Return the number of changes in the most recent call to sqlite3_exec().
    */

        public static int sqlite3_changes(sqlite3 db)
        {
            return db.nChange;
        }

        /*
    ** Return the number of changes since the database handle was opened.
    */

        public static int sqlite3_total_changes(sqlite3 db)
        {
            return db.nTotalChange;
        }

        /*
    ** Close all open savepoints. This function only manipulates fields of the
    ** database handle object, it does not close any savepoints that may be open
    ** at the b-tree/pager level.
    */

        private static void sqlite3CloseSavepoints(sqlite3 db)
        {
            while (db.pSavepoint != null)
            {
                Savepoint pTmp = db.pSavepoint;
                db.pSavepoint = pTmp.pNext;
                MemPool.DbFree(db, ref pTmp);
            }
            db.nSavepoint = 0;
            db.nStatement = 0;
            db.isTransactionSavepoint = 0;
        }

        /*
    ** Close an existing SQLite database
    */

        public static int sqlite3_close(sqlite3 db)
        {
            HashElem i;
            int j;

            if (db == null)
            {
                return StatusCode.SQLITE_OK;
            }
            if (!Utility.Sqlite3SafetyCheckSickOrOk(db))
            {
                return UnitTest.SQLITE_MISUSE_BKPT();
            }
            MutexHelper.MutexEnter(db.mutex);

            Build.ResetInternalSchema(db, 0);

            /* Tell the code in notify.c that the connection no longer holds any
      ** locks and does not require any further unlock-notify callbacks.
      */
            sqlite3ConnectionClosed(db);

            /* If a transaction is open, the ResetInternalSchema() call above
      ** will not have called the xDisconnect() method on any virtual
      ** tables in the db.aVTrans[] array. The following sqlite3VtabRollback()
      ** call will do so. We need to do this before the check for active
      ** SQL statements below, as the v-table implementation may be storing
      ** some prepared statements internally.
      */

            sqlite3VtabRollback(db);

            /* If there are any outstanding VMs, return StatusCode.SQLITE_BUSY. */
            if (db.pVdbe != null)
            {
                Utility.Sqlite3Error(db, StatusCode.SQLITE_BUSY,
                             "unable to close due to unfinalised statements");
                MutexHelper.MutexLeave(db.mutex);
                return StatusCode.SQLITE_BUSY;
            }
            Debug.Assert(Utility.Sqlite3SafetyCheckSickOrOk(db));

            for (j = 0; j < db.nDb; j++)
            {
                Btree pBt = db.aDb[j].pBt;
                if (pBt != null && sqlite3BtreeIsInBackup(pBt))
                {
                    Utility.Sqlite3Error(db, StatusCode.SQLITE_BUSY,
                                 "unable to close due to unfinished backup operation");
                    MutexHelper.MutexLeave(db.mutex);
                    return StatusCode.SQLITE_BUSY;
                }
            }

            /* Free any outstanding Savepoint structures. */
            sqlite3CloseSavepoints(db);

            for (j = 0; j < db.nDb; j++)
            {
                Db pDb = db.aDb[j];
                if (pDb.pBt != null)
                {
                    sqlite3BtreeClose(ref pDb.pBt);
                    pDb.pBt = null;
                    if (j != 1)
                    {
                        pDb.pSchema = null;
                    }
                }
            }
            Build.ResetInternalSchema(db, 0);
            Debug.Assert(db.nDb <= 2);
            Debug.Assert(db.aDb[0].Equals(db.aDbStatic[0]));
            for (j = 0; j < Utility.ArraySize(db.aFunc.a); j++)
            {
                FuncDef pNext, pHash, p;
                for (p = db.aFunc.a[j]; p != null; p = pHash)
                {
                    pHash = p.pHash;
                    while (p != null)
                    {
                        pNext = p.pNext;
                        MemPool.DbFree(db, ref p);
                        p = pNext;
                    }
                }
            }

            for (i = db.aCollSeq.first; i != null; i = i.next)
            {
//HashHelper.HashFirst(db.aCollSeq); i!=null; i=HashHelper.HashNext(i)){
                var pColl = (CollSeq[]) i.data; // HashHelper.HashData(i);
                /* Invoke any destructors registered for collation sequence user data. */
                for (j = 0; j < 3; j++)
                {
                    if (pColl[j].xDel != null)
                    {
                        pColl[j].xDel(ref pColl[j].pUser);
                    }
                }
                MemPool.DbFree(db, ref pColl);
            }
            HashHelper.HashClear(db.aCollSeq);
#if !SQLITE_OMIT_VIRTUALTABLE
for(i=HashHelper.HashFirst(&db.aModule); i; i=HashHelper.HashNext(i)){
Module pMod = (Module *)HashHelper.HashData(i);
if( pMod.xDestroy ){
pMod.xDestroy(pMod.pAux);
}
MemPool.DbFree(db,ref pMod);
}
HashHelper.HashClear(&db.aModule);
#endif

            Utility.Sqlite3Error(db, StatusCode.SQLITE_OK, 0); /* Deallocates any cached error strings. */
            if (db.pErr != null)
            {
                sqlite3ValueFree(ref db.pErr);
            }
#if !SQLITE_OMIT_LOAD_EXTENSION
            sqlite3CloseExtensions(db);
#endif

            db.magic = Const.SQLITE_MAGIC_ERROR;

            /* The temp.database schema is allocated differently from the other schema
      ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()).
      ** So it needs to be freed here. Todo: Why not roll the temp schema into
      ** the same sqliteMalloc() as the one that allocates the database
      ** structure?
      */
            MemPool.DbFree(db, ref db.aDb[1].pSchema);
            MutexHelper.MutexLeave(db.mutex);
            db.magic = Const.SQLITE_MAGIC_CLOSED;
            MutexHelper.MutexFree(ref db.mutex);
            Debug.Assert(db.lookaside.nOut == 0); /* Fails on a lookaside memory leak */
            //if ( db.lookaside.bMalloced )
            //{
            //  Malloc.sqlite3_free( ref db.lookaside.pStart );
            //}
            //Malloc.sqlite3_free( ref db );
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Rollback all database files.
    */

        private static void sqlite3RollbackAll(sqlite3 db)
        {
            int i;
            int inTrans = 0;
            Debug.Assert(MutexHelper.MutexHeld(db.mutex));
            sqlite3BeginBenignMalloc();
            for (i = 0; i < db.nDb; i++)
            {
                if (db.aDb[i].pBt != null)
                {
                    if (sqlite3BtreeIsInTrans(db.aDb[i].pBt))
                    {
                        inTrans = 1;
                    }
                    sqlite3BtreeRollback(db.aDb[i].pBt);
                    db.aDb[i].inTrans = 0;
                }
            }

            sqlite3VtabRollback(db);
            sqlite3EndBenignMalloc();
            if ((db.flags & Flag.SQLITE_InternChanges) != 0)
            {
                sqlite3ExpirePreparedStatements(db);
                Build.ResetInternalSchema(db, 0);
            }

            /* Any deferred constraint violations have now been resolved. */
            db.nDeferredCons = 0;

            /* If one has been configured, invoke the rollback-hook callback */
            if (db.xRollbackCallback != null && (inTrans != 0 || 0 == db.autoCommit))
            {
                db.xRollbackCallback(db.pRollbackArg);
            }
        }



        /*
    ** This routine implements a busy callback that sleeps and tries
    ** again until a timeout value is reached.  The timeout value is
    ** an integer number of milliseconds passed in as the first
    ** argument.
    */

        private static int sqliteDefaultBusyCallback(
            object ptr, /* Database connection */
            int count /* Number of times table has been busy */
            )
        {
#if SQLITE_OS_WIN || HAVE_USLEEP
            var delays = new byte[] {1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100};
            var totals = new byte[] {0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228};
            //# define NDELAY (delays.Length/sizeof(delays[0]))
            int NDELAY = delays.Length;
            var db = (sqlite3) ptr;
            int timeout = db.busyTimeout;
            int delay, prior;

            Debug.Assert(count >= 0);
            if (count < NDELAY)
            {
                delay = delays[count];
                prior = totals[count];
            }
            else
            {
                delay = delays[NDELAY - 1];
                prior = totals[NDELAY - 1] + delay*(count - (NDELAY - 1));
            }
            if (prior + delay > timeout)
            {
                delay = timeout - prior;
                if (delay <= 0) return 0;
            }
            sqlite3OsSleep(db.pVfs, delay*1000);
            return 1;
#else
sqlite3 db = (sqlite3)ptr;
int timeout = ( (sqlite3)ptr ).busyTimeout;
if ( ( count + 1 ) * 1000 > timeout )
{
return 0;
}
sqlite3OsSleep( db.pVfs, 1000000 );
return 1;
#endif
        }

        /*
    ** Invoke the given busy handler.
    **
    ** This routine is called when an operation failed with a lock.
    ** If this routine returns non-zero, the lock is retried.  If it
    ** returns 0, the operation aborts with an StatusCode.SQLITE_BUSY error.
    */

        private static int sqlite3InvokeBusyHandler(BusyHandler p)
        {
            int rc;
            if (UnitTest.NEVER(p == null) || p.xFunc == null || p.nBusy < 0) return 0;
            rc = p.xFunc(p.pArg, p.nBusy);
            if (rc == 0)
            {
                p.nBusy = -1;
            }
            else
            {
                p.nBusy++;
            }
            return rc;
        }

        /*
    ** This routine sets the busy callback for an Sqlite database to the
    ** given callback function with the given argument.
    */

        private static int sqlite3_busy_handler(
            sqlite3 db,
            dxBusy xBusy,
            object pArg
            )
        {
            MutexHelper.MutexEnter(db.mutex);
            db.busyHandler.xFunc = xBusy;
            db.busyHandler.pArg = pArg;
            db.busyHandler.nBusy = 0;
            MutexHelper.MutexLeave(db.mutex);
            return StatusCode.SQLITE_OK;
        }

#if !SQLITE_OMIT_PROGRESS_CALLBACK
        /*
** This routine sets the progress callback for an Sqlite database to the
** given callback function with the given argument. The progress callback will
** be invoked every nOps opcodes.
*/

        private static void sqlite3_progress_handler(
            sqlite3 db,
            int nOps,
            dxProgress xProgress, //int (xProgress)(void*),
            object pArg
            )
        {
            MutexHelper.MutexEnter(db.mutex);
            if (nOps > 0)
            {
                db.xProgress = xProgress;
                db.nProgressOps = nOps;
                db.pProgressArg = pArg;
            }
            else
            {
                db.xProgress = null;
                db.nProgressOps = 0;
                db.pProgressArg = null;
            }
            MutexHelper.MutexLeave(db.mutex);
        }
#endif


        /*
** This routine installs a default busy handler that waits for the
** specified number of milliseconds before returning 0.
*/

        public static int sqlite3_busy_timeout(sqlite3 db, int ms)
        {
            if (ms > 0)
            {
                db.busyTimeout = ms;
                sqlite3_busy_handler(db, sqliteDefaultBusyCallback, db);
            }
            else
            {
                sqlite3_busy_handler(db, null, null);
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Cause any pending operation to stop at its earliest opportunity.
    */

        private static void sqlite3_interrupt(sqlite3 db)
        {
            db.u1.isInterrupted = true;
        }


        /*
    ** This function is exactly the same as sqlite3_create_function(), except
    ** that it is designed to be called by internal code. The difference is
    ** that if a malloc() fails in sqlite3_create_function(), an error code
    ** is returned and the mallocFailed flag cleared.
    */

        private static int sqlite3CreateFunc(
            sqlite3 db,
            string zFunctionName,
            int nArg,
            byte enc,
            object pUserData,
            dxFunc xFunc, //)(sqlite3_context*,int,sqlite3_value **),
            dxStep xStep, //)(sqlite3_context*,int,sqlite3_value **),
            dxFinal xFinal //)(sqlite3_context*)
            )
        {
            FuncDef p;
            int nName;

            Debug.Assert(MutexHelper.MutexHeld(db.mutex));
            if (zFunctionName == null ||
                (xFunc != null && (xFinal != null || xStep != null)) ||
                (xFunc == null && (xFinal != null && xStep == null)) ||
                (xFunc == null && (xFinal == null && xStep != null)) ||
                (nArg < -1 || nArg > SqliteLimit.SQLITE_MAX_FUNCTION_ARG) ||
                (255 < (nName = Utility.Sqlite3Strlen30(zFunctionName))))
            {
                return UnitTest.SQLITE_MISUSE_BKPT();
            }

#if !SQLITE_OMIT_UTF16
/* If Const.SQLITE_UTF16 is specified as the encoding type, transform this
** to one of Const.SQLITE_UTF16LE or Const.SQLITE_UTF16BE using the
** SQLITE_UTF16NATIVE macro. Const.SQLITE_UTF16 is not used internally.
**
** If Const.SQLITE_ANY is specified, add three versions of the function
** to the hash table.
*/
if( enc==Const.SQLITE_UTF16 ){
enc = SQLITE_UTF16NATIVE;
}else if( enc==Const.SQLITE_ANY ){
int rc;
rc = sqlite3CreateFunc(db, zFunctionName, nArg, Const.SQLITE_UTF8,
pUserData, xFunc, xStep, xFinal);
if( rc==StatusCode.SQLITE_OK ){
rc = sqlite3CreateFunc(db, zFunctionName, nArg, Const.SQLITE_UTF16LE,
pUserData, xFunc, xStep, xFinal);
}
if( rc!=StatusCode.SQLITE_OK ){
return rc;
}
enc = Const.SQLITE_UTF16BE;
}
#else
            enc = Const.SQLITE_UTF8;
#endif

            /* Check if an existing function is being overridden or deleted. If so,
** and there are active VMs, then return StatusCode.SQLITE_BUSY. If a function
** is being overridden/deleted but there are no active VMs, allow the
** operation to continue but invalidate all precompiled statements.
*/
            p = Callback.FindFunction(db, zFunctionName, nName, nArg, enc, 0);
            if (p != null && p.iPrefEnc == enc && p.nArg == nArg)
            {
                if (db.activeVdbeCnt != 0)
                {
                    Utility.Sqlite3Error(db, StatusCode.SQLITE_BUSY,
                                 "unable to delete/modify user-function due to active statements");
                    //Debug.Assert( 0 == db.mallocFailed );
                    return StatusCode.SQLITE_BUSY;
                }
                else
                {
                    sqlite3ExpirePreparedStatements(db);
                }
            }

            p = Callback.FindFunction(db, zFunctionName, nName, nArg, enc, 1);
            Debug.Assert(p != null /*|| db.mallocFailed != 0 */);
            if (p == null)
            {
                return StatusCode.SQLITE_NOMEM;
            }
            p.flags = 0;
            p.xFunc = xFunc;
            p.xStep = xStep;
            p.xFinalize = xFinal;
            p.pUserData = pUserData;
            p.nArg = (short) nArg;
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Create new user functions.
    */

        public static int sqlite3_create_function(
            sqlite3 db,
            string zFunctionName,
            int nArg,
            byte enc,
            object p,
            dxFunc xFunc, //)(sqlite3_context*,int,sqlite3_value **),
            dxStep xStep, //)(sqlite3_context*,int,sqlite3_value **),
            dxFinal xFinal //)(sqlite3_context*)
            )
        {
            int rc;
            MutexHelper.MutexEnter(db.mutex);
            rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal);
            rc = Malloc.ApiExit(db, rc);
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }

#if !SQLITE_OMIT_UTF16
static int sqlite3_create_function16(
sqlite3 db,
string zFunctionName,
int nArg,
int eTextRep,
object p,
dxFunc xFunc,   //)(sqlite3_context*,int,sqlite3_value**),
dxStep xStep,   //)(sqlite3_context*,int,sqlite3_value**),
dxFinal xFinal  //)(sqlite3_context*)
){
int rc;
string zFunc8;
MutexHelper.MutexEnter(db.mutex);
Debug.Assert( 0==db.mallocFailed );
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal);
MemPool.DbFree(db,ref zFunc8);
rc = Malloc.ApiExit(db, rc);
MutexHelper.MutexLeave(db.mutex);
return rc;
}
#endif


        /*
** Declare that a function has been overloaded by a virtual table.
**
** If the function already exists as a regular global function, then
** this routine is a no-op.  If the function does not exist, then create
** a new one that always throws a run-time error.
**
** When virtual tables intend to provide an overloaded function, they
** should call this routine to make sure the global function exists.
** A global function must exist in order for name resolution to work
** properly.
*/

        private static int sqlite3_overload_function(
            sqlite3 db,
            string zName,
            int nArg
            )
        {
            int nName = Utility.Sqlite3Strlen30(zName);
            int rc;
            MutexHelper.MutexEnter(db.mutex);
            if (Callback.FindFunction(db, zName, nName, nArg, Const.SQLITE_UTF8, 0) == null)
            {
                sqlite3CreateFunc(db, zName, nArg, Const.SQLITE_UTF8,
                                  0, sqlite3InvalidFunction, null, null);
            }
            rc = Malloc.ApiExit(db, StatusCode.SQLITE_OK);
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }

#if !SQLITE_OMIT_TRACE
        /*
** Register a trace function.  The pArg from the previously registered trace
** is returned.
**
** A NULL trace function means that no tracing is executes.  A non-NULL
** trace is a pointer to a function that is invoked at the start of each
** SQL statement.
*/

        private static object sqlite3_trace(sqlite3 db, dxTrace xTrace, object pArg)
        {
// (*xTrace)(void*,const char*), object pArg){
            object pOld;
            MutexHelper.MutexEnter(db.mutex);
            pOld = db.pTraceArg;
            db.xTrace = xTrace;
            db.pTraceArg = pArg;
            MutexHelper.MutexLeave(db.mutex);
            return pOld;
        }

        /*
    ** Register a profile function.  The pArg from the previously registered
    ** profile function is returned.
    **
    ** A NULL profile function means that no profiling is executes.  A non-NULL
    ** profile is a pointer to a function that is invoked at the conclusion of
    ** each SQL statement that is run.
    */

        private static object sqlite3_profile(
            sqlite3 db,
            dxProfile xProfile, //void (*xProfile)(void*,const char*,sqlite_u3264),
            object pArg
            )
        {
            object pOld;
            MutexHelper.MutexEnter(db.mutex);
            pOld = db.pProfileArg;
            db.xProfile = xProfile;
            db.pProfileArg = pArg;
            MutexHelper.MutexLeave(db.mutex);
            return pOld;
        }
#endif
        // * SQLITE_OMIT_TRACE */

        /*** EXPERIMENTAL ***
**
** Register a function to be invoked when a transaction comments.
** If the invoked function returns non-zero, then the commit becomes a
** rollback.
*/

        private static object sqlite3_commit_hook(
            sqlite3 db, /* Attach the hook to this database */
            dxCommitCallback xCallback, //int (*xCallback)(void*),  /* Function to invoke on each commit */
            object pArg /* Argument to the function */
            )
        {
            object pOld;
            MutexHelper.MutexEnter(db.mutex);
            pOld = db.pCommitArg;
            db.xCommitCallback = xCallback;
            db.pCommitArg = pArg;
            MutexHelper.MutexLeave(db.mutex);
            return pOld;
        }

        /*
    ** Register a callback to be invoked each time a row is updated,
    ** inserted or deleted using this database connection.
    */

        private static object sqlite3_update_hook(
            sqlite3 db, /* Attach the hook to this database */
            dxUpdateCallback xCallback, //void (*xCallback)(void*,int,char const *,char const *,long),
            object pArg /* Argument to the function */
            )
        {
            object pRet;
            MutexHelper.MutexEnter(db.mutex);
            pRet = db.pUpdateArg;
            db.xUpdateCallback = xCallback;
            db.pUpdateArg = pArg;
            MutexHelper.MutexLeave(db.mutex);
            return pRet;
        }

        /*
    ** Register a callback to be invoked each time a transaction is rolled
    ** back by this database connection.
    */

        private static object sqlite3_rollback_hook(
            sqlite3 db, /* Attach the hook to this database */
            dxRollbackCallback xCallback, //void (*xCallback)(void*), /* Callback function */
            object pArg /* Argument to the function */
            )
        {
            object pRet;
            MutexHelper.MutexEnter(db.mutex);
            pRet = db.pRollbackArg;
            db.xRollbackCallback = xCallback;
            db.pRollbackArg = pArg;
            MutexHelper.MutexLeave(db.mutex);
            return pRet;
        }

        /*
    ** This function returns true if main-memory should be used instead of
    ** a temporary file for transient pager files and statement journals.
    ** The value returned depends on the value of db->temp_store (runtime
    ** parameter) and the compile time value of SQLITE_TEMP_STORE. The
    ** following table describes the relationship between these two values
    ** and this functions return value.
    **
    **   SQLITE_TEMP_STORE     db->temp_store     Location of temporary database
    **   -----------------     --------------     ------------------------------
    **   0                     any                file      (return 0)
    **   1                     1                  file      (return 0)
    **   1                     2                  memory    (return 1)
    **   1                     0                  file      (return 0)
    **   2                     1                  file      (return 0)
    **   2                     2                  memory    (return 1)
    **   2                     0                  memory    (return 1)
    **   3                     any                memory    (return 1)
    */

        private static bool sqlite3TempInMemory(sqlite3 db)
        {
            //#if SQLITE_TEMP_STORE==1
            if (SQLITE_TEMP_STORE == 1)
                return (db.temp_store == 2);
            //#endif
            //#if SQLITE_TEMP_STORE==2
            if (SQLITE_TEMP_STORE == 2)
                return (db.temp_store != 1);
            //#endif
            //#if SQLITE_TEMP_STORE==3
            if (SQLITE_TEMP_STORE == 3)
                return true;
            //#endif
            //#if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3
            if (SQLITE_TEMP_STORE < 1 || SQLITE_TEMP_STORE > 3)
                return false;
            //#endif
            return false;
        }

        /*
    ** This routine is called to create a connection to a database BTree
    ** driver.  If zFilename is the name of a file, then that file is
    ** opened and used.  If zFilename is the magic name ":memory:" then
    ** the database is stored in memory (and is thus forgotten as soon as
    ** the connection is closed.)  If zFilename is NULL then the database
    ** is a "virtual" database for transient use only and is deleted as
    ** soon as the connection is closed.
    **
    ** A virtual database can be either a disk file (that is automatically
    ** deleted when the file is closed) or it an be held entirely in memory.
    ** The sqlite3TempInMemory() function is used to determine which.
    */

        private static int sqlite3BtreeFactory(
            sqlite3 db, /* Main database when opening aux otherwise 0 */
            string zFilename, /* Name of the file containing the BTree database */
            bool omitJournal, /* if TRUE then do not journal this file */
            int nCache, /* How many pages in the page cache */
            int vfsFlags, /* Flags passed through to vfsOpen */
            ref Btree ppBtree /* Pointer to new Btree object written here */
            )
        {
            int btFlags = 0;
            int rc;

            Debug.Assert(MutexHelper.MutexHeld(db.mutex));
            //Debug.Assert( ppBtree != null);
            if (omitJournal)
            {
                btFlags |= BtreeOpenFlag.BTREE_OMIT_JOURNAL;
            }
            if ((db.flags & Flag.SQLITE_NoReadlock) != 0)
            {
                btFlags |= BtreeOpenFlag.BTREE_NO_READLOCK;
            }
#if !SQLITE_OMIT_MEMORYDB
            if (String.IsNullOrEmpty(zFilename) && sqlite3TempInMemory(db))
            {
                zFilename = ":memory:";
            }
#endif
            // * SQLITE_OMIT_MEMORYDB */

            if ((vfsFlags & FileOpenOperation.SQLITE_OPEN_MAIN_DB) != 0 && (zFilename == null))
            {
// || *zFilename==0) ){
                vfsFlags = (vfsFlags & ~FileOpenOperation.SQLITE_OPEN_MAIN_DB) | FileOpenOperation.SQLITE_OPEN_TEMP_DB;
            }
            rc = sqlite3BtreeOpen(zFilename, db, ref ppBtree, btFlags, vfsFlags);
            /* If the B-Tree was successfully opened, set the pager-cache size to the
      ** default value. Except, if the call to BtreeOpen() returned a handle
      ** open on an existing shared pager-cache, do not change the pager-cache
      ** size.
      */
            if (rc == StatusCode.SQLITE_OK && null == sqlite3BtreeSchema(ppBtree, 0, null))
            {
                sqlite3BtreeSetCacheSize(ppBtree, nCache);
            }
            return rc;
        }

        /*
    ** Return UTF-8 encoded English language explanation of the most recent
    ** error.
    */

        public static string sqlite3_errmsg(sqlite3 db)
        {
            string z;
            if (db == null)
            {
                return Utility.ErrorString(StatusCode.SQLITE_NOMEM);
            }
            if (!Utility.Sqlite3SafetyCheckSickOrOk(db))
            {
                return Utility.ErrorString(UnitTest.SQLITE_MISUSE_BKPT());
            }
            MutexHelper.MutexEnter(db.mutex);
            //if ( db.mallocFailed != 0 )
            //{
            //  z = Utility.ErrorString( StatusCode.SQLITE_NOMEM );
            //}
            //else
            {
                z = sqlite3_value_text(db.pErr);
                //Debug.Assert( 0 == db.mallocFailed );
                if (String.IsNullOrEmpty(z))
                {
                    z = Utility.ErrorString(db.errCode);
                }
            }
            MutexHelper.MutexLeave(db.mutex);
            return z;
        }

#if !SQLITE_OMIT_UTF16
/*
** Return UTF-16 encoded English language explanation of the most recent
** error.
*/
const void *sqlite3_errmsg16(sqlite3 *db){
static const ushort outOfMem[] = {
'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0
};
static const ushort misuse[] = {
'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ',
'r', 'o', 'u', 't', 'i', 'n', 'e', ' ',
'c', 'a', 'l', 'l', 'e', 'd', ' ',
'o', 'u', 't', ' ',
'o', 'f', ' ',
's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0
};

const void *z;
if( !db ){
return (void *)outOfMem;
}
if( !Utility.Sqlite3SafetyCheckSickOrOk(db) ){
return (void *)misuse;
}
MutexHelper.MutexEnter(db->mutex);
if( db->mallocFailed ){
z = (void *)outOfMem;
}else{
z = sqlite3_value_text16(db->pErr);
if( z==0 ){
sqlite3ValueSetStr(db->pErr, -1, Utility.ErrorString(db->errCode),
Const.SQLITE_UTF8, Const.SQLITE_STATIC);
z = sqlite3_value_text16(db->pErr);
}
/* A malloc() may have failed within the call to sqlite3_value_text16()
** above. If this is the case, then the db->mallocFailed flag needs to
** be cleared before returning. Do this directly, instead of via
** Malloc.ApiExit(), to avoid setting the database handle error message.
*/
db->mallocFailed = 0;
}
MutexHelper.MutexLeave(db->mutex);
return z;
}
#endif
        // * SQLITE_OMIT_UTF16 */

        /*
** Return the most recent error code generated by an SQLite routine. If NULL is
** passed to this function, we assume a malloc() failed during sqlite3_open().
*/

        public static int sqlite3_errcode(sqlite3 db)
        {
            if (db != null && !Utility.Sqlite3SafetyCheckSickOrOk(db))
            {
                return UnitTest.SQLITE_MISUSE_BKPT();
            }
            if (null == db /*|| db.mallocFailed != 0 */)
            {
                return StatusCode.SQLITE_NOMEM;
            }
            return db.errCode & db.errMask;
        }

        private static int sqlite3_extended_errcode(sqlite3 db)
        {
            if (db != null && !Utility.Sqlite3SafetyCheckSickOrOk(db))
            {
                return UnitTest.SQLITE_MISUSE_BKPT();
            }
            if (null == db /*|| db.mallocFailed != 0 */)
            {
                return StatusCode.SQLITE_NOMEM;
            }
            return db.errCode;
        }

        /*
    ** Create a new collating function for database "db".  The name is zName
    ** and the encoding is enc.
    */

        private static int createCollation(
            sqlite3 db,
            string zName,
            byte enc,
            byte collType,
            object pCtx,
            dxCompare xCompare, //)(void*,int,const void*,int,const void*),
            dxDelCollSeq xDel //)(void*)
            )
        {
            CollSeq pColl;
            int enc2;
            int nName = Utility.Sqlite3Strlen30(zName);

            Debug.Assert(MutexHelper.MutexHeld(db.mutex));

            /* If Const.SQLITE_UTF16 is specified as the encoding type, transform this
      ** to one of Const.SQLITE_UTF16LE or Const.SQLITE_UTF16BE using the
      ** SQLITE_UTF16NATIVE macro. Const.SQLITE_UTF16 is not used internally.
      */
            enc2 = enc;
            UnitTest.TestCase(enc2 == Const.SQLITE_UTF16);
            UnitTest.TestCase(enc2 == Const.SQLITE_UTF16_ALIGNED);
            if (enc2 == Const.SQLITE_UTF16 || enc2 == Const.SQLITE_UTF16_ALIGNED)
            {
                enc2 = SQLITE_UTF16NATIVE;
            }
            if (enc2 < Const.SQLITE_UTF8 || enc2 > Const.SQLITE_UTF16BE)
            {
                return UnitTest.SQLITE_MISUSE_BKPT();
            }

            /* Check if this call is removing or replacing an existing collation
      ** sequence. If so, and there are active VMs, return busy. If there
      ** are no active VMs, invalidate any pre-compiled statements.
      */
            pColl = Callback.FindCollSeq(db, (byte) enc2, zName, 0);
            if (pColl != null && pColl.xCmp != null)
            {
                if (db.activeVdbeCnt != 0)
                {
                    Utility.Sqlite3Error(db, StatusCode.SQLITE_BUSY,
                                 "unable to delete/modify collation sequence due to active statements");
                    return StatusCode.SQLITE_BUSY;
                }
                sqlite3ExpirePreparedStatements(db);

                /* If collation sequence pColl was created directly by a call to
        ** sqlite3_create_collation, and not generated by synthCollSeq(),
        ** then any copies made by synthCollSeq() need to be invalidated.
        ** Also, collation destructor - CollSeq.xDel() - function may need
        ** to be called.
        */
                if ((pColl.enc & ~Const.SQLITE_UTF16_ALIGNED) == enc2)
                {
                    var aColl = (CollSeq[]) HashHelper.HashFind(db.aCollSeq, zName, nName);
                    int j;
                    for (j = 0; j < 3; j++)
                    {
                        CollSeq p = aColl[j];
                        if (p.enc == pColl.enc)
                        {
                            if (p.xDel != null)
                            {
                                p.xDel(ref p.pUser);
                            }
                            p.xCmp = null;
                        }
                    }
                }
            }

            pColl = Callback.FindCollSeq(db, (byte) enc2, zName, 1);
            if (pColl != null)
            {
                pColl.xCmp = xCompare;
                pColl.pUser = pCtx;
                pColl.xDel = xDel;
                pColl.enc = (byte) (enc2 | (enc & Const.SQLITE_UTF16_ALIGNED));
                pColl.type = collType;
            }
            Utility.Sqlite3Error(db, StatusCode.SQLITE_OK, 0);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** This array defines hard upper bounds on limit values.  The
    ** initializer must be kept in sync with the SQLITE_LIMIT_*
    ** #defines in sqlite3.h.
    */

        private static readonly int[] aHardLimit = new[]
                                                       {
                                                           SqliteLimit.SQLITE_MAX_LENGTH,
                                                           SqliteLimit.SQLITE_MAX_SQL_LENGTH,
                                                           SqliteLimit.SQLITE_MAX_COLUMN,
                                                           SqliteLimit.SQLITE_MAX_EXPR_DEPTH,
                                                           SqliteLimit.SQLITE_MAX_COMPOUND_SELECT,
                                                           SqliteLimit.SQLITE_MAX_VDBE_OP,
                                                           SqliteLimit.SQLITE_MAX_FUNCTION_ARG,
                                                           Const.SQLITE_MAX_ATTACHED,
                                                           SqliteLimit.SQLITE_MAX_LIKE_PATTERN_LENGTH,
                                                           SqliteLimit.SQLITE_MAX_VARIABLE_NUMBER,
                                                           SqliteLimit.SQLITE_MAX_TRIGGER_DEPTH,
                                                       };

        /*
    ** Make sure the hard limits are set to reasonable values
    */
        //#if SQLITE_MAX_LENGTH<100
        //# error SqliteLimit.SQLITE_MAX_LENGTH must be at least 100
        //#endif
        //#if SQLITE_MAX_SQL_LENGTH<100
        //# error SqliteLimit.SQLITE_MAX_SQL_LENGTH must be at least 100
        //#endif
        //#if SQLITE_MAX_SQL_LENGTH>SqliteLimit.SQLITE_MAX_LENGTH
        //# error SqliteLimit.SQLITE_MAX_SQL_LENGTH must not be greater than SqliteLimit.SQLITE_MAX_LENGTH
        //#endif
        //#if SQLITE_MAX_COMPOUND_SELECT<2
        //# error SqliteLimit.SQLITE_MAX_COMPOUND_SELECT must be at least 2
        //#endif
        //#if SQLITE_MAX_VDBE_OP<40
        //# error SqliteLimit.SQLITE_MAX_VDBE_OP must be at least 40
        //#endif
        //#if SQLITE_MAX_FUNCTION_ARG<0 || SqliteLimit.SQLITE_MAX_FUNCTION_ARG>1000
        //# error SqliteLimit.SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
        //#endif
        //#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30
        //# error SQLITE_MAX_ATTACHED must be between 0 and 30
        //#endif
        //#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
        //# error SqliteLimit.SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
        //#endif
        //#if SQLITE_MAX_COLUMN>32767
        //# error SqliteLimit.SQLITE_MAX_COLUMN must not exceed 32767
        //#endif
        //#if SQLITE_MAX_TRIGGER_DEPTH<1
        //# error SqliteLimit.SQLITE_MAX_TRIGGER_DEPTH must be at least 1
        //#endif

        /*
    ** Change the value of a limit.  Report the old value.
    ** If an invalid limit index is supplied, report -1.
    ** Make no changes but still report the old value if the
    ** new limit is negative.
    **
    ** A new lower limit does not shrink existing constructs.
    ** It merely prevents new constructs that exceed the limit
    ** from forming.
    */

        private static int sqlite3_limit(sqlite3 db, int limitId, int newLimit)
        {
            int oldLimit;
            if (limitId < 0 || limitId >= Const.SQLITE_N_LIMIT)
            {
                return -1;
            }
            oldLimit = db.aLimit[limitId];
            if (newLimit >= 0)
            {
                if (newLimit > aHardLimit[limitId])
                {
                    newLimit = aHardLimit[limitId];
                }
                db.aLimit[limitId] = newLimit;
            }
            return oldLimit;
        }

        /*
    ** This routine does the work of opening a database on behalf of
    ** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
    ** is UTF-8 encoded.
    */

        private static int openDatabase(
            string zFilename, /* Database filename UTF-8 encoded */
            ref sqlite3 ppDb, /* OUT: Returned database handle */
            unsigned flags, /* Operational flags */
            string zVfs /* Name of the VFS to use */
            )
        {
            sqlite3 db;
            int rc;
            int isThreadsafe;

            ppDb = null;
#if !SQLITE_OMIT_AUTOINIT
            rc = sqlite3_initialize();
            if (rc != 0) return rc;
#endif

            if (Global.Config.bCoreMutex == false)
            {
                isThreadsafe = 0;
            }
            else if ((flags & FileOpenOperation.SQLITE_OPEN_NOMUTEX) != 0)
            {
                isThreadsafe = 0;
            }
            else if ((flags & FileOpenOperation.SQLITE_OPEN_FULLMUTEX) != 0)
            {
                isThreadsafe = 1;
            }
            else
            {
                isThreadsafe = Global.Config.bFullMutex ? 1 : 0;
            }
            if ((flags & FileOpenOperation.SQLITE_OPEN_PRIVATECACHE) != 0)
            {
                flags &= ~FileOpenOperation.SQLITE_OPEN_SHAREDCACHE;
            }
            else if (Global.Config.sharedCacheEnabled)
            {
                flags |= FileOpenOperation.SQLITE_OPEN_SHAREDCACHE;
            }

            /* Remove harmful bits from the flags parameter
      **
      ** The FileOpenOperation.SQLITE_OPEN_NOMUTEX and FileOpenOperation.SQLITE_OPEN_FULLMUTEX flags were
      ** dealt with in the previous code block.  Besides these, the only
      ** valid input flags for sqlite3_open_v2() are FileOpenOperation.SQLITE_OPEN_READONLY,
      ** FileOpenOperation.SQLITE_OPEN_READWRITE, and FileOpenOperation.SQLITE_OPEN_CREATE.  Silently mask
      ** off all other flags.
      */
            flags &= ~(FileOpenOperation.SQLITE_OPEN_DELETEONCLOSE |
                       FileOpenOperation.SQLITE_OPEN_EXCLUSIVE |
                       FileOpenOperation.SQLITE_OPEN_MAIN_DB |
                       FileOpenOperation.SQLITE_OPEN_TEMP_DB |
                       FileOpenOperation.SQLITE_OPEN_TRANSIENT_DB |
                       FileOpenOperation.SQLITE_OPEN_MAIN_JOURNAL |
                       FileOpenOperation.SQLITE_OPEN_TEMP_JOURNAL |
                       FileOpenOperation.SQLITE_OPEN_SUBJOURNAL |
                       FileOpenOperation.SQLITE_OPEN_MASTER_JOURNAL |
                       FileOpenOperation.SQLITE_OPEN_NOMUTEX |
                       FileOpenOperation.SQLITE_OPEN_FULLMUTEX
                      );


            /* Allocate the sqlite data structure */
            db = new sqlite3(); //Malloc.MallocZero( sqlite3.Length );
            if (db == null) goto opendb_out;
            if (Global.Config.bFullMutex && isThreadsafe != 0)
            {
                db.mutex = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_RECURSIVE);
                if (db.mutex == null)
                {
                    //Malloc.sqlite3_free( ref db );
                    goto opendb_out;
                }
            }
            MutexHelper.MutexEnter(db.mutex);
            db.errMask = 0xff;
            db.nDb = 2;
            db.magic = Const.SQLITE_MAGIC_BUSY;
            Array.Copy(db.aDbStatic, db.aDb, db.aDbStatic.Length); // db.aDb = db.aDbStatic;
            Debug.Assert(db.aLimit.Length == aHardLimit.Length);
            Buffer.BlockCopy(aHardLimit, 0, db.aLimit, 0, aHardLimit.Length*sizeof (int));
                //memcpy(db.aLimit, aHardLimit, sizeof(db.aLimit));
            db.autoCommit = 1;
            db.nextAutovac = -1;
            db.nextPagesize = 0;
            db.flags |= Flag.SQLITE_ShortColNames;
            if (Const.SQLITE_DEFAULT_FILE_FORMAT < 4)
                db.flags |= Flag.SQLITE_LegacyFileFmt
#if  SQLITE_ENABLE_LOAD_EXTENSION
| Flag.SQLITE_LoadExtension
#endif
#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS
                 | Flag.SQLITE_RecTriggers
#endif
                    ;
            HashHelper.HashInit(db.aCollSeq);
#if !SQLITE_OMIT_VIRTUALTABLE
HashHelper.HashInit( ref db.aModule );
#endif
            db.pVfs = sqlite3_vfs_find(zVfs);
            if (db.pVfs == null)
            {
                rc = StatusCode.SQLITE_ERROR;
                Utility.Sqlite3Error(db, rc, "no such vfs: %s", zVfs);
                goto opendb_out;
            }

            /* Add the default collation sequence BINARY. BINARY works for both UTF-8
      ** and UTF-16, so add a version for each to avoid any unnecessary
      ** conversions. The only error that can occur here is a malloc() failure.
      */
            createCollation(db, "BINARY", Const.SQLITE_UTF8, CollSeqType.SQLITE_COLL_BINARY, 0,
                            binCollFunc, null);
            createCollation(db, "BINARY", Const.SQLITE_UTF16BE, CollSeqType.SQLITE_COLL_BINARY, 0,
                            binCollFunc, null);
            createCollation(db, "BINARY", Const.SQLITE_UTF16LE, CollSeqType.SQLITE_COLL_BINARY, 0,
                            binCollFunc, null);
            createCollation(db, "RTRIM", Const.SQLITE_UTF8, CollSeqType.SQLITE_COLL_USER, 1,
                            binCollFunc, null);
            //if ( db.mallocFailed != 0 )
            //{
            //  goto opendb_out;
            //}
            db.pDfltColl = Callback.FindCollSeq(db, Const.SQLITE_UTF8, "BINARY", 0);
            Debug.Assert(db.pDfltColl != null);

            /* Also add a UTF-8 case-insensitive collation sequence. */
            createCollation(db, "NOCASE", Const.SQLITE_UTF8, CollSeqType.SQLITE_COLL_NOCASE, 0,
                            nocaseCollatingFunc, null);

            /* Open the backend database driver */
            db.openFlags = flags;
            rc = sqlite3BtreeFactory(db, zFilename, false, Const.SQLITE_DEFAULT_CACHE_SIZE,
                                     flags | FileOpenOperation.SQLITE_OPEN_MAIN_DB,
                                     ref db.aDb[0].pBt);
            if (rc != StatusCode.SQLITE_OK)
            {
                if (rc == ExtendedResultCode.SQLITE_IOERR_NOMEM)
                {
                    rc = StatusCode.SQLITE_NOMEM;
                }
                Utility.Sqlite3Error(db, rc, 0);
                goto opendb_out;
            }
            db.aDb[0].pSchema = Callback.SchemaGet(db, db.aDb[0].pBt);
            db.aDb[1].pSchema = Callback.SchemaGet(db, null);


            /* The default safety_level for the main database is 'full'; for the temp
      ** database it is 'NONE'. This matches the pager layer defaults.
      */
            db.aDb[0].zName = "main";
            db.aDb[0].safety_level = 3;
            db.aDb[1].zName = "temp";
            db.aDb[1].safety_level = 1;

            db.magic = Const.SQLITE_MAGIC_OPEN;
            //if ( db.mallocFailed != 0 )
            //{
            //  goto opendb_out;
            //}

            /* Register all built-in functions, but do not attempt to read the
      ** database schema yet. This is delayed until the first time the database
      ** is accessed.
      */
            Utility.Sqlite3Error(db, StatusCode.SQLITE_OK, 0);
            sqlite3RegisterBuiltinFunctions(db);

            /* Load automatic extensions - extensions that have been registered
      ** using the sqlite3_automatic_extension() API.
      */
            sqlite3AutoLoadExtensions(db);
            rc = sqlite3_errcode(db);
            if (rc != StatusCode.SQLITE_OK)
            {
                goto opendb_out;
            }


#if  SQLITE_ENABLE_FTS1
if( 0==db.mallocFailed ){
extern int sqlite3Fts1Init(sqlite3*);
rc = sqlite3Fts1Init(db);
}
#endif

#if  SQLITE_ENABLE_FTS2
if( 0==db.mallocFailed && rc==StatusCode.SQLITE_OK ){
extern int sqlite3Fts2Init(sqlite3*);
rc = sqlite3Fts2Init(db);
}
#endif

#if  SQLITE_ENABLE_FTS3
if( 0==db.mallocFailed && rc==StatusCode.SQLITE_OK ){
rc = sqlite3Fts3Init(db);
}
#endif

#if  SQLITE_ENABLE_ICU
if( 0==db.mallocFailed && rc==StatusCode.SQLITE_OK ){
extern int sqlite3IcuInit(sqlite3*);
rc = sqlite3IcuInit(db);
}
#endif

#if SQLITE_ENABLE_RTREE
if( 0==db.mallocFailed && rc==StatusCode.SQLITE_OK){
rc = sqlite3RtreeInit(db);
}
#endif

            Utility.Sqlite3Error(db, rc, 0);

            /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
      ** mode.  -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
      ** mode.  Doing nothing at all also makes NORMAL the default.
      */
#if  SQLITE_DEFAULT_LOCKING_MODE
db.dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE;
sqlite3PagerLockingMode(sqlite3BtreePager(db.aDb[0].pBt),
SQLITE_DEFAULT_LOCKING_MODE);
#endif

            /* Enable the lookaside-malloc subsystem */
            setupLookaside(db, null, Global.Config.szLookaside,
                           Global.Config.nLookaside);

            opendb_out:
            if (db != null)
            {
                Debug.Assert(db.mutex != null || isThreadsafe == 0 || !Global.Config.bFullMutex);
                MutexHelper.MutexLeave(db.mutex);
            }
            rc = sqlite3_errcode(db);
            if (rc == StatusCode.SQLITE_NOMEM)
            {
                sqlite3_close(db);
                db = null;
            }
            else if (rc != StatusCode.SQLITE_OK)
            {
                db.magic = Const.SQLITE_MAGIC_SICK;
            }
            ppDb = db;
            return Malloc.ApiExit(0, rc);
        }

        /*
    ** Open a new database handle.
    */

        public static int sqlite3_open(
            string zFilename,
            ref sqlite3 ppDb
            )
        {
            return openDatabase(zFilename, ref ppDb,
                                FileOpenOperation.SQLITE_OPEN_READWRITE | FileOpenOperation.SQLITE_OPEN_CREATE, null);
        }

        public static int sqlite3_open_v2(
            string filename, /* Database filename (UTF-8) */
            ref sqlite3 ppDb, /* OUT: SQLite db handle */
            int flags, /* Flags */
            string zVfs /* Name of VFS module to use */
            )
        {
            return openDatabase(filename, ref ppDb, flags, zVfs);
        }

#if !SQLITE_OMIT_UTF16

/*
** Open a new database handle.
*/
int sqlite3_open16(
const void *zFilename,
sqlite3 **ppDb
){
char const *zFilename8;   /* zFilename encoded in UTF-8 instead of UTF-16 */
sqlite3_value pVal;
int rc;

Debug.Assert(zFilename );
Debug.Assert(ppDb );
*ppDb = 0;
#if !SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc !=0) return rc;
#endif
pVal = sqlite3ValueNew(0);
sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, Const.SQLITE_STATIC);
zFilename8 = sqlite3ValueText(pVal, Const.SQLITE_UTF8);
if( zFilename8 ){
rc = openDatabase(zFilename8, ppDb,
FileOpenOperation.SQLITE_OPEN_READWRITE | FileOpenOperation.SQLITE_OPEN_CREATE, 0);
Debug.Assert(*ppDb || rc==StatusCode.SQLITE_NOMEM );
if( rc==StatusCode.SQLITE_OK && !Helper.DbHasProperty(*ppDb, 0, DBSchemaFlag.DB_SchemaLoaded) ){
Helper.ENC(*ppDb) = SQLITE_UTF16NATIVE;
}
}else{
rc = StatusCode.SQLITE_NOMEM;
}
sqlite3ValueFree(pVal);

return Malloc.ApiExit(0, rc);
}
#endif
        // * SQLITE_OMIT_UTF16 */

        /*
** Register a new collation sequence with the database handle db.
*/

        private static int sqlite3_create_collation(
            sqlite3 db,
            string zName,
            int enc,
            object pCtx,
            dxCompare xCompare
            )
        {
            int rc;
            MutexHelper.MutexEnter(db.mutex);
            //Debug.Assert( 0 == db.mallocFailed );
            rc = createCollation(db, zName, (byte) enc, CollSeqType.SQLITE_COLL_USER, pCtx, xCompare, null);
            rc = Malloc.ApiExit(db, rc);
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }

        /*
    ** Register a new collation sequence with the database handle db.
    */

        private static int sqlite3_create_collation_v2(
            sqlite3 db,
            string zName,
            int enc,
            object pCtx,
            dxCompare xCompare, //int(*xCompare)(void*,int,const void*,int,const void*),
            dxDelCollSeq xDel //void(*xDel)(void*)
            )
        {
            int rc;
            MutexHelper.MutexEnter(db.mutex);
            //Debug.Assert( 0 == db.mallocFailed );
            rc = createCollation(db, zName, (byte) enc, CollSeqType.SQLITE_COLL_USER, pCtx, xCompare, xDel);
            rc = Malloc.ApiExit(db, rc);
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }

#if !SQLITE_OMIT_UTF16
/*
** Register a new collation sequence with the database handle db.
*/
//int sqlite3_create_collation16(
//  sqlite3* db,
//  string zName,
//  int enc,
//  void* pCtx,
//  int(*xCompare)(void*,int,const void*,int,const void*)
//){
//  int rc = StatusCode.SQLITE_OK;
//  char *zName8;
//  MutexHelper.MutexEnter(db.mutex);
//  Debug.Assert( 0==db.mallocFailed );
//  zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE);
//  if( zName8 ){
//    rc = createCollation(db, zName8, (byte)enc, CollSeqType.SQLITE_COLL_USER, pCtx, xCompare, 0);
//    MemPool.DbFree(db,ref zName8);
//  }
//  rc = Malloc.ApiExit(db, rc);
//  MutexHelper.MutexLeave(db.mutex);
//  return rc;
//}
#endif
        // * SQLITE_OMIT_UTF16 */

        /*
** Register a collation sequence factory callback with the database handle
** db. Replace any previously installed collation sequence factory.
*/

        private static int sqlite3_collation_needed(
            sqlite3 db,
            object pCollNeededArg,
            dxCollNeeded xCollNeeded
            )
        {
            MutexHelper.MutexEnter(db.mutex);
            db.xCollNeeded = xCollNeeded;
            db.xCollNeeded16 = null;
            db.pCollNeededArg = pCollNeededArg;
            MutexHelper.MutexLeave(db.mutex);
            return StatusCode.SQLITE_OK;
        }

#if !SQLITE_OMIT_UTF16
/*
** Register a collation sequence factory callback with the database handle
** db. Replace any previously installed collation sequence factory.
*/
//int sqlite3_collation_needed16(
//  sqlite3 db,
//  void pCollNeededArg,
//  void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
//){
//  MutexHelper.MutexEnter(db.mutex);
//  db.xCollNeeded = 0;
//  db.xCollNeeded16 = xCollNeeded16;
//  db.pCollNeededArg = pCollNeededArg;
//  MutexHelper.MutexLeave(db.mutex);
//  return StatusCode.SQLITE_OK;
//}
#endif
        // * SQLITE_OMIT_UTF16 */

#if !SQLITE_OMIT_GLOBALRECOVER
#if !SQLITE_OMIT_DEPRECATED
    /*
** This function is now an anachronism. It used to be used to recover from a
** malloc() failure, but SQLite now does this automatically.
*/
    static int sqlite3_global_recover()
    {
      return StatusCode.SQLITE_OK;
    }
#endif
#endif

        /*
** Test to see whether or not the database connection is in autocommit
** mode.  Return TRUE if it is and FALSE if not.  Autocommit mode is on
** by default.  Autocommit is disabled by a BEGIN statement and reenabled
** by the next COMMIT or ROLLBACK.
**
******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
*/

        private static byte sqlite3_get_autocommit(sqlite3 db)
        {
            return db.autoCommit;
        }

     

#if !SQLITE_OMIT_DEPRECATED
    /*
** This is a convenience routine that makes sure that all thread-specific
** data for this thread has been deallocated.
**
** SQLite no longer uses thread-specific data so this routine is now a
** no-op.  It is retained for historical compatibility.
*/
    void sqlite3_thread_cleanup()
    {
    }
#endif
        /*
** Return meta information about a specific column of a database table.
** See comment in sqlite3.h (sqlite.h.in) for details.
*/
#if SQLITE_ENABLE_COLUMN_METADATA

int sqlite3_table_column_metadata(
sqlite3 db,            /* Connection handle */
string zDbName,        /* Database name or NULL */
string zTableName,     /* Table name */
string zColumnName,    /* Column name */
ref byte[] pzDataType, /* OUTPUT: Declared data type */
ref byte[] pzCollSeq,  /* OUTPUT: Collation sequence name */
ref int pNotNull,      /* OUTPUT: True if NOT NULL constraint exists */
ref int pPrimaryKey,   /* OUTPUT: True if column part of PK */
ref int pAutoinc       /* OUTPUT: True if column is auto-increment */
){
int rc;
string zErrMsg = "";
Table pTab = null;
Column pCol = null;
int iCol;

char const *zDataType = 0;
char const *zCollSeq = 0;
int notnull = 0;
int primarykey = 0;
int autoinc = 0;

/* Ensure the database schema has been loaded */
MutexHelper.MutexEnter(db.mutex);
sqlite3BtreeEnterAll(db);
rc = sqlite3Init(db, zErrMsg);
if( StatusCode.SQLITE_OK!=rc ){
goto error_out;
}

/* Locate the table in question */
pTab = Build.FindTable(db, zTableName, zDbName);
if( null==pTab || pTab.pSelect ){
pTab = 0;
goto error_out;
}

/* Find the column for which info is requested */
if( ExprHelper.IsRowid(zColumnName) ){
iCol = pTab.iPKey;
if( iCol>=0 ){
pCol = pTab.aCol[iCol];
}
}else{
for(iCol=0; iCol<pTab.nCol; iCol++){
pCol = pTab.aCol[iCol];
if( 0==Utility.Sqlite3StrICmp(pCol.zName, zColumnName) ){
break;
}
}
if( iCol==pTab.nCol ){
pTab = 0;
goto error_out;
}
}

/* The following block stores the meta information that will be returned
** to the caller in local variables zDataType, zCollSeq, notnull, primarykey
** and autoinc. At this point there are two possibilities:
**
**     1. The specified column name was rowid", "oid" or "_rowid_"
**        and there is no explicitly declared IPK column.
**
**     2. The table is not a view and the column name identified an
**        explicitly declared column. Copy meta information from pCol.
*/
if( pCol ){
zDataType = pCol.zType;
zCollSeq = pCol.zColl;
notnull = pCol->notNull!=0;
primarykey  = pCol->isPrimKey!=0;
autoinc = pTab.iPKey==iCol && (pTab.tabFlags & TabFlag.TF_Autoincrement)!=0;
}else{
zDataType = "INTEGER";
primarykey = 1;
}
if( !zCollSeq ){
zCollSeq = "BINARY";
}

error_out:
sqlite3BtreeLeaveAll(db);

/* Whether the function call succeeded or failed, set the output parameters
** to whatever their local counterparts contain. If an error did occur,
** this has the effect of zeroing all output parameters.
*/
if( pzDataType ) pzDataType = zDataType;
if( pzCollSeq ) pzCollSeq = zCollSeq;
if( pNotNull ) pNotNull = notnull;
if( pPrimaryKey ) pPrimaryKey = primarykey;
if( pAutoinc ) pAutoinc = autoinc;

if( StatusCode.SQLITE_OK==rc && !pTab ){
MemPool.DbFree(db, ref zErrMsg);
zErrMsg = Print.MPrintf(db, "no such table column: %s.%s", zTableName,
zColumnName);
rc = StatusCode.SQLITE_ERROR;
}
Utility.Sqlite3Error(db, rc, (zErrMsg?"%s":0), zErrMsg);
MemPool.DbFree(db, ref zErrMsg);
rc = Malloc.ApiExit(db, rc);
MutexHelper.MutexLeave(db.mutex);
return rc;
}
#endif

        /*
** Sleep for a little while.  Return the amount of time slept.
*/

        public static int sqlite3_sleep(int ms)
        {
            sqlite3_vfs pVfs;
            int rc;
            pVfs = sqlite3_vfs_find(null);
            if (pVfs == null) return 0;

            /* This function works in milliseconds, but the underlying OsSleep()
      ** API uses microseconds. Hence the 1000's.
      */
            rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000);
            return rc;
        }

        /*
    ** Enable or disable the extended result codes.
    */

        private static int sqlite3_extended_result_codes(sqlite3 db, bool onoff)
        {
            MutexHelper.MutexEnter(db.mutex);
            db.errMask = (int) (onoff ? 0xffffffff : 0xff);
            MutexHelper.MutexLeave(db.mutex);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Invoke the xFileControl method on a particular database.
    */

        private static int sqlite3_file_control(sqlite3 db, string zDbName, int op, ref int pArg)
        {
            int rc = StatusCode.SQLITE_ERROR;
            int iDb;
            MutexHelper.MutexEnter(db.mutex);
            if (zDbName == null)
            {
                iDb = 0;
            }
            else
            {
                for (iDb = 0; iDb < db.nDb; iDb++)
                {
                    if (db.aDb[iDb].zName == zDbName) break;
                }
            }
            if (iDb < db.nDb)
            {
                Btree pBtree = db.aDb[iDb].pBt;
                if (pBtree != null)
                {
                    Pager pPager;
                    sqlite3_file fd;
                    sqlite3BtreeEnter(pBtree);
                    pPager = sqlite3BtreePager(pBtree);
                    Debug.Assert(pPager != null);
                    fd = sqlite3PagerFile(pPager);
                    Debug.Assert(fd != null);
                    if (fd.pMethods != null)
                    {
                        rc = sqlite3OsFileControl(fd, (uint) op, ref pArg);
                    }
                    sqlite3BtreeLeave(pBtree);
                }
            }
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }

        /*
    ** Interface to the testing logic.
    */

        private static int sqlite3_test_control(int op, params object[] ap)
        {
            int rc = 0;
#if !SQLITE_OMIT_BUILTIN_TEST
            //  va_list ap;
            Custom.VaStart(ap, "op");
            switch (op)
            {
                    /*
        ** Save the current state of the PRNG.
        */
                case SQLITE_TESTCTRL_PRNG_SAVE:
                    {
                        Random.PrngSaveState();
                        break;
                    }

                    /*
        ** Restore the state of the PRNG to the last state saved using
        ** PRNG_SAVE.  If PRNG_SAVE has never before been called, then
        ** this verb acts like PRNG_RESET.
        */
                case SQLITE_TESTCTRL_PRNG_RESTORE:
                    {
                        Random.PrngRestoreState();
                        break;
                    }

                    /*
        ** Reset the PRNG back to its uninitialized state.  The next call
        ** to Random.Randomness() will reseed the PRNG using a single call
        ** to the xRandomness method of the default VFS.
        */
                case SQLITE_TESTCTRL_PRNG_RESET:
                    {
                        Random.PrngResetState();
                        break;
                    }

                    /*
        **  sqlite3_test_control(BITVEC_TEST, size, program)
        **
        ** Run a test against a Bitvec object of size.  The program argument
        ** is an array of integers that defines the test.  Return -1 on a
        ** memory allocation error, 0 on success, or non-zero for an error.
        ** See the sqlite3BitvecBuiltinTest() for additional information.
        */
                case SQLITE_TESTCTRL_BITVEC_TEST:
                    {
                        var sz = (int) Custom.VaArg(ap, "int");
                        var aProg = (int[]) Custom.VaArg(ap, "int[]");
                        rc = sqlite3BitvecBuiltinTest((uint) sz, aProg);
                        break;
                    }

                    /*
        **  sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
        **
        ** Register hooks to call to indicate which malloc() failures
        ** are benign.
        */
                case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS:
                    {
                        //typedef void (*void_function)(void);
                        void_function xBenignBegin;
                        void_function xBenignEnd;
                        xBenignBegin = (void_function) Custom.VaArg(ap, "void_function");
                        xBenignEnd = (void_function) Custom.VaArg(ap, "void_function");
                        sqlite3BenignMallocHooks(xBenignBegin, xBenignEnd);
                        break;
                    }
                    /*
        **  sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, unsigned int X)
        **
        ** Set the PENDING byte to the value in the argument, if X>0.
        ** Make no changes if X==0.  Return the value of the pending byte
        ** as it existing before this routine was called.
        **
        ** IMPORTANT:  Changing the PENDING byte from 0x40000000 results in
        ** an incompatible database file format.  Changing the PENDING byte
        ** while any database connection is open results in undefined and
        ** dileterious behavior.
        */
                case SQLITE_TESTCTRL_PENDING_BYTE:
                    {
                        var newVal = (uint) Custom.VaArg(ap, "uint");
                        rc = Global.Sqlite3PendingByte;
                        if (newVal != 0)
                        {
                            if (Global.Sqlite3PendingByte != newVal)
                                Global.Sqlite3PendingByte = (int) newVal;
#if DEBUG && !NO_TCL
              TCLsqlite3PendingByte.iValue = Global.Sqlite3PendingByte;
#endif
                            Global.PENDING_BYTE = Global.Sqlite3PendingByte;
                        }
                        break;
                    }

                    /*
        **  sqlite3_test_control(SQLITE_TESTCTRL_ASSERT, int X)
        **
        ** This action provides a run-time test to see whether or not
        ** assert() was enabled at compile-time.  If X is true and assert()
        ** is enabled, then the return value is true.  If X is true and
        ** assert() is disabled, then the return value is zero.  If X is
        ** false and assert() is enabled, then the assertion fires and the
        ** process aborts.  If X is false and assert() is disabled, then the
        ** return value is zero.
        */
                case SQLITE_TESTCTRL_ASSERT:
                    {
                        int x = 0;
                        Debug.Assert((x = (int) Custom.VaArg(ap, "int")) != 0);
                        rc = x;
                        break;
                    }


                    /*
        **  sqlite3_test_control(SQLITE_TESTCTRL_ALWAYS, int X)
        **
        ** This action provides a run-time test to see how the ALWAYS and
        ** NEVER macros were defined at compile-time.
        **
        ** The return value is UnitTest.ALWAYS(X).
        **
        ** The recommended test is X==2.  If the return value is 2, that means
        ** UnitTest.ALWAYS() and UnitTest.NEVER() are both no-op pass-through macros, which is the
        ** default setting.  If the return value is 1, then UnitTest.ALWAYS() is either
        ** hard-coded to true or else it asserts if its argument is false.
        ** The first behavior (hard-coded to true) is the case if
        ** SQLITE_TESTCTRL_ASSERT shows that assert() is disabled and the second
        ** behavior (assert if the argument to UnitTest.ALWAYS() is false) is the case if
        ** SQLITE_TESTCTRL_ASSERT shows that assert() is enabled.
        **
        ** The run-time test procedure might look something like this:
        **
        **    if( sqlite3_test_control(SQLITE_TESTCTRL_ALWAYS, 2)==2 ){
        **      // UnitTest.ALWAYS() and UnitTest.NEVER() are no-op pass-through macros
        **    }else if( sqlite3_test_control(SQLITE_TESTCTRL_ASSERT, 1) ){
        **      // UnitTest.ALWAYS(x) asserts that x is true. UnitTest.NEVER(x) asserts x is false.
        **    }else{
        **      // UnitTest.ALWAYS(x) is a constant 1.  UnitTest.NEVER(x) is a constant 0.
        **    }
        */
                case SQLITE_TESTCTRL_ALWAYS:
                    {
                        var x = (int) Custom.VaArg(ap, "int");
                        rc = UnitTest.ALWAYS(x);
                        break;
                    }

                    /*   sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
        **
        ** Set the nReserve size to N for the main database on the database
        ** connection db.
        */
                case SQLITE_TESTCTRL_RESERVE:
                    {
                        var db = (sqlite3) Custom.VaArg(ap, "sqlite3");
                        var x = (int) Custom.VaArg(ap, "int");
                        MutexHelper.MutexEnter(db.mutex);
                        sqlite3BtreeSetPageSize(db.aDb[0].pBt, 0, x, 0);
                        MutexHelper.MutexLeave(db.mutex);
                        break;
                    }

                    /*  sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
        **
        ** Enable or disable various optimizations for testing purposes.  The 
        ** argument N is a bitmask of optimizations to be disabled.  For normal
        ** operation N should be 0.  The idea is that a test program (like the
        ** SQL Logic Test or SLT test module) can run the same SQL multiple times
        ** with various optimizations disabled to verify that the same answer
        ** is obtained in every case.
        */
                case SQLITE_TESTCTRL_OPTIMIZATIONS:
                    {
                        var db = (sqlite3) Custom.VaArg(ap, "sqlite3"); //sqlite3 *db = Custom.VaArg(ap, sqlite3*);
                        var x = (int) Custom.VaArg(ap, "int"); //int x = Custom.VaArg(ap,int);
                        db.flags = (x & TestControlFlag.SQLITE_OptMask) | (db.flags & ~TestControlFlag.SQLITE_OptMask);
                        break;
                    }

                    //#ifdef SQLITE_N_KEYWORD
                    /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
        **
        ** If zWord is a keyword recognized by the parser, then return the
        ** number of keywords.  Or if zWord is not a keyword, return 0.
        ** 
        ** This test feature is only available in the amalgamation since
        ** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite
        ** is built using separate source files.
        */
                case SQLITE_TESTCTRL_ISKEYWORD:
                    {
                        var zWord = (string) Custom.VaArg(ap, "char*");
                        int n = Utility.Sqlite3Strlen30(zWord);
                        rc = (KeywordHash.KeywordCode(zWord, n) != TokenKeyword.TK_ID) ? KeywordHash.SQLITE_N_KEYWORD : 0;
                        break;
                    }
                    //#endif 
            }
            Custom.VaEnd(ap);
#endif
            //* SQLITE_OMIT_BUILTIN_TEST */
            return rc;
        }
    }
}